#pragma once

#ifndef __ENV__STRING__HH__
#define __ENV__STRING__HH__

#include <string>
#include <iostream>
#include <sstream>
#include <qstring.h>
#include <qstringlist.h>
#include <vector>
#include <map>

class String;
class StringVector;

extern bool operator == ( const std::string &, const String &rk );
extern bool operator != ( const std::string &, const String &rk );

extern std::ostream& operator << ( std::ostream&, const String &rk );

extern String operator + ( const std::string&, const String &rk );
extern String operator + ( const char *, const String &rk );

class String
{
	friend bool operator == ( const std::string &, const String &rk );
	friend bool operator != ( const std::string &, const String &rk );

	friend std::ostream& operator << ( std::ostream&, const String &rk );

	friend String operator + ( const std::string&, const String &rk );
	friend String operator + ( const char *, const String &rk );

	std::string d;
public:
	static size_t npos;
	
	template<typename T>
	String( const T &val ){ std::stringstream ss; ss << val; d=ss.str(); }
	String(void );
	String( const char* str );
	
	String( const String &str ):d(str.d){}
	template<> String( const std::string &str ):d(str){}
	template<> String( const char &str ):d(1,str){}
	template<> String( const QString &str ):d(str.toUtf8().data()){}
	~String(void);
	
	bool operator == ( const String &rk );
	bool operator == ( const QString &rk );
	bool operator == ( const char* rk );
	bool operator == ( const std::string &rk );

	bool operator != ( const String &rk );
	bool operator != ( const QString &rk );
	bool operator != ( const char* rk );
	bool operator != ( const std::string &rk );
	
	void clear();
	bool empty() const;
	size_t size() const;
	
	const std::string& data() const;
	std::string& data();
	std::string str( const std::string &s);

	void insert( const size_t pos, const String &str_ );
	bool replace( const String & what, const String& by );
	
	String& operator += ( const char*rk );
	String& operator += ( const QString &rk );
	String& operator += ( const String &rk );
	String& operator += ( const std::string &rk );
	
	String operator + ( const char*rk ) const;
	String operator + ( const String &rk ) const;
	String operator + ( const QString &rk ) const;
	String operator + ( const std::string &rk ) const;
	
	template<typename T>
	String& operator = ( const T &val ){ std::stringstream ss; ss << val; d=ss.str(); return *this; }
	String& operator = ( const QString &rk );
	String& operator = ( const String &rk );
	String& operator = ( const std::string &rk );
	String& operator = ( const char*rk );
	
	const char* cstr() const;
	
	operator std::string() const{ return d;  }
	operator QString() const{ return QString::fromUtf8(d.c_str());  }

	char at( const size_t i ) const;
	bool found( const String &sub, size_t fromPos = 0 )const;

	size_t find(const String& _Right, size_t _Off = 0) const{ return d.find( _Right.d, _Off ); }

	size_t find(const char *_Ptr,	size_t _Off, size_t _Count) const{ return d.find( _Ptr, _Off, _Count ); }
	size_t find(const char *_Ptr, size_t _Off = 0) const{ return d.find( _Ptr, _Off ); }
	size_t find(char _Ch, size_t _Off = 0) const{ return d.find( _Ch, _Off ); }
	size_t rfind(const String& _Right, size_t _Off = npos) const { return d.find( _Right, _Off ); }
	size_t rfind(const char *_Ptr,	size_t _Off, size_t _Count) const{ return d.find( _Ptr, _Off, _Count ); }
	size_t rfind(const char *_Ptr, size_t _Off = npos) const{ return d.find( _Ptr, _Off ); }
	size_t rfind(char _Ch, size_t _Off = npos) const{ return d.find( _Ch, _Off ); }
	size_t find_first_of(const String& _Right,	size_t _Off = 0) const { return d.find( _Right, _Off ); }

	size_t find_first_of(const char *_Ptr,	size_t _Off, size_t _Count) const{ return d.find( _Ptr, _Off, _Count ); }
	size_t find_first_of(const char *_Ptr,	size_t _Off = 0) const{ return d.find( _Ptr, _Off ); }
	size_t find_first_of(char _Ch,	size_t _Off = 0) const{ return d.find( _Ch, _Off ); }

	size_t find_last_of(const String& _Right,	size_t _Off = npos) const{ return d.find( _Right, _Off ); }
	size_t find_last_of(const char *_Ptr,	size_t _Off, size_t _Count) const{ return d.find( _Ptr, _Off, _Count ); }
	size_t find_last_of(const char *_Ptr,	size_t _Off = npos) const{ return d.find( _Ptr, _Off ); }

	size_t find_last_of(char _Ch,	size_t _Off = npos) const{ return d.find( _Ch, _Off ); }
	size_t find_first_not_of(const String& _Right,	size_t _Off = 0) const{ return d.find( _Right, _Off ); }
	size_t find_first_not_of(const char *_Ptr,	size_t _Off, size_t _Count) const{ return d.find( _Ptr, _Off, _Count ); }
	size_t find_first_not_of(const char *_Ptr,	size_t _Off = 0) const{ return d.find( _Ptr, _Off ); }

	size_t find_first_not_of(char _Ch,	size_t _Off = 0) const{ return d.find( _Ch, _Off ); }
	size_t find_last_not_of(const String& _Right,size_t _Off = npos) const { return d.find( _Right, _Off ); }

	size_t find_last_not_of(const char *_Ptr,size_t _Off, size_t _Count) const{ return d.find( _Ptr, _Off, _Count ); }

	size_t find_last_not_of(const char *_Ptr,size_t _Off = npos) const{ return d.find( _Ptr, _Off ); }

	size_t find_last_not_of(char _Ch,size_t _Off = npos) const{ return d.find( _Ch, _Off ); }

	void remove( const size_t id, const size_t count = 1 );
	String substr( const size_t pos, const size_t count = std::string::npos ) const;
	String trimmed() const;
	
	bool operator<( const String & rk ) const{ return d < rk.d; }
	bool operator<=( const String & rk ) const{ return d <= rk.d; }
	bool operator>( const String & rk ) const{ return d > rk.d; }
	bool operator>=( const String & rk ) const{ return d >= rk.d; }

	bool operator<( const std::string & rk ) const{ return d < rk; }
	bool operator<=( const std::string & rk ) const{ return d <= rk; }
	bool operator>( const std::string & rk ) const{ return d > rk; }
	bool operator>=( const std::string & rk ) const{ return d >= rk; }

	StringVector split( const String &token );
};

#define TOQT( std_str ) QString::fromUtf8( String(std_str).cstr() )
#define FROMQT( qt_str ) String(QString(qt_str).toUtf8().data())

class StringVector : public std::vector<String>
{
public:
	typedef std::vector<String> Type;
	typedef String				Item;

	StringVector(){}
	StringVector( const Item& rk ){ push_back( rk ); }
	StringVector( const Type& rk ) : Type( rk ){}
	StringVector( const StringVector& rk ): Type( rk ){}

#if defined(QTOOLS_GUI)
	StringVector( const QStringList& rk ){ clear(); for(auto i=rk.begin(); i!=rk.end(); i++ ) push_back(FROMQT(*i)); }
	StringVector& operator << ( const QString& rk ){ return push_back( String(rk) ); }
	StringVector& operator += ( const QString& rk ){ return push_back( String(rk) ); }
	StringVector& operator=( const QStringList& rk ){ clear();for(auto i=rk.begin(); i!=rk.end(); i++ )push_back(FROMQT(*i)); return *this; }
#endif
	StringVector& push_back( const StringVector& rk ){ for( auto i=rk.begin(); i!=rk.end(); i++ ) push_back(*i); return *this; }
	StringVector& push_back( const String& rk ){ Type::push_back(rk.data()); return *this; }

	StringVector& operator << ( const StringVector& rk ){ return push_back( rk ); }
	StringVector& operator += ( const StringVector& rk ){ return push_back( rk ); }
	StringVector& operator << ( const String& rk ){ return push_back( rk.data() ); }
	StringVector& operator += ( const String& rk ){ return push_back( rk.data() ); }
	StringVector& operator << ( const std::string& rk ){ return push_back( rk ); }
	StringVector& operator += ( const std::string& rk ){ return push_back( rk  ); }
	StringVector& operator << ( const char* rk ){ return push_back( String(rk) ); }
	StringVector& operator += ( const char* rk ){ return push_back( String(rk) ); }

	operator QStringList() const{ QStringList lst; for( auto i=begin(); i!=end(); i++ ) lst << String(*i); return lst; }

	String join( const String & _link = " ", const size_t from = 0 ) const
	{
		String ret;
		for( size_t i=0; i<size(); i++ )
			ret += at(i) + (((i+1)==size())?String():_link);
		return ret;
	}
	static StringVector split( const String &str, const String &tokens )
	{
		size_t p = 0, old = 0;
		StringVector ret;
		while( (p=str.find(tokens.data(), p)) != std::string::npos )
		{
			String sub(str.substr( old, p-old ));
			ret.push_back( sub );
			p+=tokens.size();
			old = p;
		}
		return ret;
	}
	StringVector& operator=( const Type& rk ){ assign( rk.begin(), rk.end() ); return *this;}
	StringVector& operator=( const StringVector& rk ){ assign( rk.begin(), rk.end() ); return *this;}
};

class StringMap : public std::map<String, String>
{
public:
	typedef std::map<String, String>		Type;
	typedef String							Item;

	StringMap(){}
	StringMap( const String &key, const String &value ){ at(key) = value; }
	StringMap( const std::map<String, String> & rk ) : Type( rk ){}
	StringMap( const StringMap& rk ) : Type(rk){}
	
	StringVector join( const String& key_sep = ":=", const size_t from = 0 ) const
	{
		StringVector ret;
		for( auto i=begin(); i!=end(); i++ )
			ret.push_back( String( i->first + key_sep + i->second ) );
		return ret;
	}
	static StringMap split( const String &str, const String &key_sep = ":=", const String &pair_sep = "\r\n" )
	{
		size_t p = 0, old = 0;
		StringMap ret;
		StringVector pairs(StringVector::split( str, pair_sep ));
		StringVector::iterator it(pairs.begin());
		while( it!=pairs.end() )
		{
			StringVector val(StringVector::split( str, key_sep ));
			if( !val.empty() && !val[0].trimmed().empty() )
				ret[val[0]] = val.join( key_sep, 1 );
			it++;
		}
		return ret;
	}
	StringMap& assign( Type::const_iterator begin_, Type::const_iterator end_ )
	{
		clear();
		for( Type::const_iterator i=begin_; i!=end_; i++ )
			at( i->first ) = i->second;
		return *this;
	}
	StringMap& operator=( const StringMap& rk ){ assign( rk.begin(), rk.end() ); return *this;}
	StringMap& operator=( const Type& rk ){ assign( rk.begin(), rk.end() ); return *this; }
};

#endif